整合python高产与C/C++高效的优势
利用C或Python已有功能服务彼此
python作为胶水语言整合或者被整合到各类独立程序
扩展:python中调用C/C++编写的库
提升关键代码性能
引入C语言成熟功能库
以python为主程序,C通过.dll/.so形式使用
方式:
- ctypes - 调用DLL或共享库的python功能函数库,标准库API - https://docs.python.org/3.7/library/ctypes.html - 通过一个python标准库实现python扩展 - C语言功能编成.dll或.so库,加载库及调用函数,API - C语言独立编程,python使用库调用接口函数 
- CFFI (C Foreign Function Interface for Python) - 在python中直接使用C函数的方式 - 第三方库 pip install cffi - https://cffi.readthedocs.io/en/latest/ - 思路类似ctypes,使用API扩展C程序,也可以直接混合编程 - 关注C函数的访问接口,而不是库函数,构建API - C语言独立编程,python用CFFI扩展,学习成本低。 - 功能接口
 
| 与C语言数据类型相关的接口 | 描述 | 
|---|---|
| ffi.NULL | 相当于常量值NULL | 
| ffi.new(cdecl) | 数组或指针的生成,new(‘x*’)或new(‘x[n]’) | 
| ffi.cast(ctype, value) | C数据类型的声明,ctype是类型名,value是变量名 cast(‘int’, x) | 
| ffi.string(cdata) | 从cdata类型中返回一个Python字符串 | 
| ffi.unpack(cdata, length) | 从cdata数组中获取特定长度,返回一个Python字符串或列表 | 
| 与数据大小相关的接口 | 描述 | 
|---|---|
| ffi.typeof(ctype) | 返回ctype的长度 | 
| ffi.sizeof(object) | 返回object对象的长度 | 
| ffi.alignof(ctype) | 返回ctype或对象的长度 | 
| 与调用相关的接口 | 描述 | 
|---|---|
| ffi.dlopen(libpath) | 打开动态链接库并建立一个句柄 | 
| ffi.dlclose(lib) | 关闭动态链接库并释放句柄 | 
| ffi.cdef(str) | str指明Python中需要使用的C类型、函数等声明 | 
| 与内存操作相关的接口 | 描述 | 
|---|---|
| ffi.memmove(dst, src, n) | 从src向dst拷贝n直接内容,src和dst都是python变量 | 
- 简单应用 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13- /* 可编译为.dll的C语言代码 mxmul.h */ 
 DLLIMPORT int *mxmul(int nrow, int nk, int ncol, int mx1[][nk], int mx2[][ncol]);- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48- /* 可编译为.dll的C语言代码 mxmul.c */ 
 /* Parameters: nrow, nk, ncol, mx1, mx2 */
 DLLIMPORT int *mxmul(int nrow, int nk, int ncol, int mx1[][nk], int mx2[][ncol])
 {
 int x, i, j;
 int *rst;
 rst = malloc(sizeof(int) * nrow * ncol);
 for(i = 0; i < nrow; i++)
 {
 for(j = 0; j < ncol; j++)
 {
 rst[i*ncol +j] =0;
 for(x = 0; x < nk; x++)
 {
 rest[i*ncol +j] +=*(*(mx1+i)+x) * *(*(mx2+x)+j);
 }
 }
 }
 return rst;
 }
 BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
 {
 switch(fdwReason)
 {
 case DLL_PROCESS_ATTACH:
 {
 break;
 }
 case DLL_PROCESS_DETACH:
 {
 break;
 }
 case DLL_THREAD_ATTACH:
 {
 break;
 }
 case DLL_THREAD_DETACH:
 {
 break;
 }
 }
 }- mxmul.c和mxmul.h 编译后可以得到mxmul.dll和mxmul32.dll - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42- # 用来封装.dll的python模块 cmxmul.py 
 import array
 from cffi import FFI
 def cmxmul(nrow, nk, ncol, mx1, mx2):
 ffi = FFI()
 # 基本类型参数关联
 c_nrow = ffi.cast('int', nrow)
 c_nk = ffi.cast('int', nk)
 c_ncol = ffi.cast('int', ncol)
 # 列表类型参数关联
 _mx1 = array.array('l')
 _mx2 = array.array('l')
 [_mx1.fromlist(x) for x in mx1 ]
 [_mx2.fromlist(x) for x in mx2 ]
 # 数据的内存拷贝
 c_mx1 = ffi.new('int[]', len(_mx1))
 c_mx2 = ffi.new('int[]', len(_mx2))
 ffi.memmove(c_mx1, _mx1, ffi.sizeof(c_mx1))
 ffi.memmove(c_mx2, _mx2, ffi.sizeof(c_mx2))
 # 声明函数
 ffi.cdef('''
 int *mxmul(int nrow, int nk, int ncol, int *mx1, int *mx2);
 ''')
 # 调用dll库
 try :
 # 64位计算机加载mxmul.dll
 C = ffi.dlopen('mxmul.dll')
 except :
 # 32位计算机加载mxmul32.dll
 C = ffi.dlopen('mxmul32.dll')
 # 调用动态链接库中的函数
 c_res = C.mxmul(c_nrow, c_nk, c_ncol, c_mx1, c_mx2)
 #解包返回
 return ffi.unpack(c_res, nrow * ncol- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17- # 用来测试效果的python程序 test.py 
 from cmxmul import cmxmul
 if __name__ == '__main__':
 import time
 nrow, nk, ncol = 500, 300, 500
 mx1 = [[i for i in range(nk)] for j in range(nrow)]
 mx2 = [[i for i in range(ncol)] for j in range(nk)]
 start = time.perf_counter()
 rst = cmxmul(nrow, nk, ncol, mx1, mx2)
 end = time.perf_counter()
 print('运行时间:%.4f秒' % (end - start))
 '''
 运行时间:0.3135秒
 '''
- Cython - 实现python扩展的一种语言,第三方库。 - 通过一种简单的语言来实现python和C的接口 - 采用了Pyrex语法形式 - 采用C数据类型的python编程,实现混合编程 
- SWIG - 一个将C/C++与脚本语言相整合的编译器,独立工具 - 通过一个编译器来实现python和C的接口 - 纯C/C++编程,通过编写接口变成python模块 - 独立C和python编程,重点在于编写接口(描述) 
调用:python和C间以进程级别相互调用
模块间功能互用
以功能使用为目标
C/C++和python都是独立程序
方式:子进程或线程方式
- Python 中调用C语言程序 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13- /* 待调用的c程序源码 */ 
 int main(int argc, char **argv)
 {
 int a;
 a = atoi(argv[1]);
 printf("input x:%d\n", a);
 printf("pow(x):%d\n", a*a);
 return 0;
 }
 //编译成pow.exe- 1 
 2
 3
 4
 5
 6
 7- # xxx.py 
 import subprocess
 subprocess.run(["pow.exe", "9"])
 '''执行后的结果
 input x:9
 pow(x):81
 '''- subprocess.run()的参数可以是一个字符串或一个列表 - 如果是字符串,就是程序的名称 - 如果是列表,是程序名称和参数组成的列表 
- C语言中调用python程序 - 1 
 2
 3
 4- # pow.py 
 import sys
 print("input x:%d"%sys.argv[1])
 print("pow(x):%d"%pow(int(sys.argv[1]),2))- 1 
 2
 3
 4
 5
 6
 7
 8- /* test.c */ 
 int main(){
 system("python pow.py 9");
 return 0;
 }
 //编译后运行- system(char *cmd) 是C/C++下的标准函数,C89定义。将指令和参数以字符指针形式作为参数传递执行程序。 
嵌入:C/C++中调用python程序
利用python高产
引入python成熟功能库
以C/C++为主程序,python通过源文件形式使用
方式:python/c API https://docs.python.org/3/c-api/
- 嵌入python语句:嵌入一个或多个python语句
- 嵌入python脚本:嵌入一个或多个python文件
- python/C API 需要加载python解释器以及加载python语句和脚本
头文件:python.h
函数:加载python解释器、嵌入python语句及脚本、数据类型转换等。
| 1 | 
 | 
| 函数 | 描述 | 
|---|---|
| Py_Initialize() | 初始化Python解释器,加载builtins、__main__、sys等 | 
| Py_Finalize() | 终结化python解释器,释放解释器占用的内存 | 
| PyRun_SimpleString(const char* cmd) | 在__main__模块中执行一句语句 如果main不存在则创建 | 
| PyRun_SimpleFile(FILE fp, const char fname) | 在C中调用一个Python文件 | 
| 1 | 
 |